#include "stdafx.h"
#include "indicator.h"

static const QColor indicator_color = QColor(Qt::white);
static const QColor line_color = QColor(Qt::white);//QColor(QColor(195, 195, 195));
static const QColor editmode_border_color(QColor(63, 135, 132));
static const QColor displayed_color(QColor(0, 255, 0, 30));
static const QColor none_displayed_color(QColor(255, 0, 0, 30));
static const int selection_width = 4;
static const QRectF item_size = QRectF(-50, -8, 100, 16);

Indicator::Indicator(Common* _common, SceneView* _scene, automaticItem* _parent, const QPointF &_position_relative_source, const bool &_visible, const QStringList &_parameters)
	: common(_common), scene(_scene), parent(_parent), position_relative_source(_position_relative_source)
{
	editmode = false;
	setFlag(ItemIsMovable);
	setFlag(ItemIsSelectable);
	setFlag(ItemSendsGeometryChanges);
	setCacheMode(DeviceCoordinateCache);
	addParameters(_parameters);
	
	scene->addItem(this);
	size = QRectF(item_size.x(), item_size.y(), item_size.width(), item_size.height()*countDisplayedParameters());
	line = new QGraphicsLineItem;
	line->setZValue(-1);
	line->setPen(line_color);
	setIndicatorVisible(_visible);
	setLineVisible(false);
	updateLinePosition();
	setPos(parent->pos() - position_relative_source);
	scene->addItem(line);
	setZValue(-1);
}

Indicator::~Indicator(){
	delete line;
	for (hash_map<string, IndicatorItem*>::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
		delete it->second;
}

void Indicator::addParameters(const QStringList &_parameters){
	foreach(QString parameter, _parameters){
		QString short_name = g_units.getShortName(parameter);
		QString default_unit = common->getDefaultUnitText(parameter);
		IndicatorItem *indicatorItem = new IndicatorItem(scene, this, parameter, short_name, 0, default_unit, true);
		connect(indicatorItem, SIGNAL(createTrend(const QString&)), this, SLOT(trend(const QString&)));
		list_of_parameters[parameter.toStdString()] = indicatorItem;
	}
}


void Indicator::setIndicatorVisible(const bool &_visible){
	if (_visible){
		scene->addItem(this);
		for (hash_map<string, IndicatorItem*>::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
			scene->addItem(it->second);
	}else{
		scene->removeItem(this);
		for (hash_map<string, IndicatorItem*>::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
			scene->removeItem(it->second);
	}
	this->setVisible(_visible);
/*
	for (hash_map<const QString*, IndicatorItem*>::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
		it->second->setVisible(_visible);*/
	setLineVisible(_visible);
}

void Indicator::setLineVisible(bool _visible){
	if (this->isVisible() && _visible){
		scene->addItem(line);
	}else{
		scene->removeItem(line);
	}
	line->setVisible(this->isVisible() && _visible);
}

int Indicator::countDisplayedParameters(){
	int count = 0;
	for (hash_map<string, IndicatorItem*>::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
		if(it->second->display)
				count++;
	return count;
}

QRectF Indicator::boundingRect() const{
	return size.normalized().adjusted(-selection_width, -selection_width, selection_width, selection_width);
}

void Indicator::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *){
	
	if (editmode){
		painter->setRenderHint(QPainter::Antialiasing);
		QPen pen;
		pen.setColor(editmode_border_color);
		pen.setWidth(7);
		painter->setPen(pen);
		painter->drawRect(size.normalized().adjusted(-selection_width, -selection_width, selection_width, selection_width));
		setFlag(ItemIsMovable, false);
	}
}

QVariant Indicator::itemChange(GraphicsItemChange change, const QVariant &value){
	if(change == ItemPositionHasChanged){
		updateItemPosition();
		updateLinePosition();
	}
	if(change == ItemSelectedHasChanged){
		if (!this->isSelected() && editmode){
			setEditMode(false);
			setFlag(ItemIsMovable);
		}
	}
	update();
	return QGraphicsItem::itemChange(change, value);
}

void Indicator::mousePressEvent(QGraphicsSceneMouseEvent *event){
	if(editmode){
		int i = 0;
		for (hash_map<string, IndicatorItem*>::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++) {
			if ( this->pos().y()  + item_size.y() + item_size.height() * i < event->scenePos().y() && this->pos().y()  + item_size.y() + item_size.height() * (i + 1) > event->scenePos().y()){
				bool itemVisible = it->second->display;
				if (!(itemVisible && countDisplayedParameters() == 1)){
					it->second->display = !itemVisible;
					it->second->update();
					break;
				}
			}
			i++;
		}
	}
	update();
	QGraphicsItem::mousePressEvent(event);
}

void Indicator::mouseReleaseEvent(QGraphicsSceneMouseEvent *event){
	update_position_relative_source();
	update();
	QGraphicsItem::mouseReleaseEvent(event);
}

void Indicator::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event){
	setEditMode(true);
	update();
	QGraphicsItem::mouseMoveEvent(event);
}

void Indicator::update_position_relative_source(){
	position_relative_source.setX(parent->pos().x() - this->pos().x());
	position_relative_source.setY(parent->pos().y() - this->pos().y());
}

void Indicator::contextMenuEvent(QGraphicsSceneContextMenuEvent *event){
	
}

void Indicator::adjust(const QPointF &_pos){
	setX(_pos.x() - position_relative_source.x());
	setY(_pos.y() - position_relative_source.y());
	updateLinePosition();
}

void Indicator::updateItemPosition(){
	int index = 0;
	for (hash_map<string, IndicatorItem*>::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
		if(it->second->display || editmode)
			it->second->setPos(this->pos().x(), this->pos().y() + item_size.height() * index++);
}

void Indicator::updateLinePosition(){
	if (size.height() == 0)	return;
	QPointF source_point = parent->pos(), dest_point = QPointF();
	double cur_length;
	for (double i = this->pos().x() + size.x(); i <= this->pos().x()  + size.x() + size.width(); i+=size.width()/2){
		for (double j = this->pos().y() + size.y(); j <= this->pos().y() + size.y() + size.height(); j+=size.height()/2){
			if (dest_point == QPointF()){
				dest_point = QPointF(i, j);
				cur_length = getDistance(source_point, dest_point);
			}else{
				if (cur_length >  getDistance(source_point, QPointF(i, j))){
					dest_point = QPointF(i, j);
					cur_length = getDistance(source_point, dest_point);
				}
			}
		}
	}
	if (!parent->isVisible()){
		QRectF _size = parent->boundingRect();
		source_point += QPointF(0, _size.height() - ( _size.width() / 2));
	}
	line->setLine(source_point.x(), source_point.y(), dest_point.x(), dest_point.y());
}

double Indicator::getDistance(const QPointF &_p1, const QPointF &_p2){
	return pow(pow(_p1.x() - _p2.x(), 2) + pow(_p1.y() - _p2.y(), 2), 0.5);
}

void Indicator::setEditMode(const bool &_b){
	editmode = _b;
	for (hash_map<string, IndicatorItem*>::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
		it->second->setEditMode(_b);
	if (editmode){
		size = QRectF(item_size.x(), item_size.y(), item_size.width(), item_size.height() * list_of_parameters.size());
	}else{
		size = QRectF(item_size.x(), item_size.y(), item_size.width(), item_size.height() * countDisplayedParameters());
	}
	updateItemPosition();
	updateLinePosition();
}

QString Indicator::getAllParameters(){
	QString data;
	for (hash_map<string, IndicatorItem*>::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++){
		if(data != 0) data += "//";
		QString name = QString::fromStdString(it->first);
		data += name + ":";
		IndicatorItem* patameter = it->second;
		data += patameter->short_name + ";";
		data += patameter->unit + ";";
		data += patameter->display == true ? "visible" : "hidden";
	}
	return data;
}

void Indicator::setAllParameters(const QString &_data){
	list_of_parameters.clear();
	QStringList list = _data.split("//");
	for(int i = 0; i < list.size(); i++){
		QString full_name =  list[i].split(":").front();
		QStringList parameter = list[i].split(":").back().split(";");
		if (parameter.size() != 3) return;
		QString short_name = parameter[0];
		QString unit = parameter[1];
		bool display = parameter[2] == "visible";
		QString name = list[i].split(":").front();
		IndicatorItem *indicatorItem = new IndicatorItem(scene, this, full_name, short_name, 0, unit, display); 
		connect(indicatorItem, SIGNAL(createTrend(const QString&)), this, SLOT(trend(const QString&)));
		list_of_parameters[name.toStdString()] = indicatorItem; 
	}
	size = QRectF(item_size.x(), item_size.y(), item_size.width(), item_size.height() * countDisplayedParameters());

	updateItemPosition();
	updateLinePosition();
}

void Indicator::updateParameters(const QStringList &_parameters, const QStringList &_units, const QVector<int> &_digits){
	QStringList parameter_list = _parameters;
	
	for (hash_map<string, IndicatorItem*>::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); ){
		QString p = QString::fromStdString(it->first);
		bool find_parameter = false;
		foreach(QString parameter, parameter_list)
			if (parameter == p){
				find_parameter = true;
				break;
			}
		
		if (!find_parameter){
			scene->removeItem(it->second);
			it = list_of_parameters.erase(it);	
		}else{
			parameter_list.erase(find(parameter_list.begin(), parameter_list.end(), p));
			++it;
		}
	}
	addParameters(parameter_list);
	for(int i = 0; i < _parameters.size(); ++i){
		for (hash_map<string, IndicatorItem*>::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++){
			if (QString::fromStdString(it->first) == _parameters[i]){
				it->second->setDigit(_digits[i]);
				it->second->setUnit(_units[i]);
			}
		}
	}
	size = QRectF(item_size.x(), item_size.y(), item_size.width(), item_size.height() * countDisplayedParameters());
	setLineVisible(list_of_parameters.size() != 0);
	/*if(list_of_parameters.size() == 0){
		scene->removeItem(line);
	}else{
		scene->addItem(line);
	}*/
	updateItemPosition();
	updateLinePosition();
}

void Indicator::trend(const QString &_parameter){
	emit createTrend(_parameter);
}

void Indicator::setParameterValue(const QString _parameter, const double &_value, const int &_digits){
	for (hash_map<string,  IndicatorItem*>::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++){
		if (QString::fromStdString(it->first) == _parameter){
			it->second->setValue(_value, _digits);
		}
	}
}

bool Indicator::lineIsVisible(){
	return line->isVisible();
}

//========================================================================================

IndicatorItem::IndicatorItem(SceneView* _scene, Indicator *_indicator, const QString &_full_name,const QString &_short_name, const double &_value, const QString &_unit, const bool &_display)
	: scene(_scene), indicator(_indicator), full_name(_full_name), short_name(_short_name), value(_value), unit(_unit), display(_display)
{
	setFlag(ItemSendsGeometryChanges);
	setCacheMode(DeviceCoordinateCache);
	if (_display) scene->addItem(this);
	setVisible(_display);
	editmode = false;
	digits = 2;
}

IndicatorItem::~IndicatorItem(){
	//scene.clear();	
}

QRectF IndicatorItem::boundingRect() const{
	return item_size;
}

void IndicatorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *){
	painter->setRenderHint(QPainter::Antialiasing);
	painter->setBrush(Qt::NoBrush);
	if(editmode && display)
		painter->setBrush(displayed_color);
	if(editmode && !display)
		painter->setBrush(none_displayed_color);
	QPen pen;
	pen.setColor(indicator_color);
	pen.setWidth(1);
	painter->setPen(pen);
	double coeff = 1.4;
	QRectF short_name_rect = QRectF(item_size.x(), item_size.y(), item_size.height() * coeff, item_size.height());
	QRectF value_rect = QRectF(item_size.x() + item_size.height()  * coeff, item_size.y(), item_size.width() - item_size.height() * coeff, item_size.height());
	QFont font;
	font.setPointSize(8);
	//font.setBold(true);
	painter->setFont(font);
	painter->drawText(short_name_rect, Qt::AlignCenter, short_name);
	painter->drawText(value_rect.normalized().adjusted(0, 0, -2, 0), Qt::AlignRight,
		QString::number(value, 'f', digits)+ " " + unit);
	painter->drawRect(short_name_rect);
	painter->drawRect(value_rect);
}

QVariant IndicatorItem::itemChange(GraphicsItemChange change, const QVariant &value){
	if(change == ItemPositionHasChanged){

	}
	if(change == ItemSelectedHasChanged){

	}
	return QGraphicsItem::itemChange(change, value);
}

void IndicatorItem::mousePressEvent(QGraphicsSceneMouseEvent *event){

	update();
	QGraphicsItem::mousePressEvent(event);
}

void IndicatorItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event){

	update();
	QGraphicsItem::mouseReleaseEvent(event);
}

void IndicatorItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event){

	update();
	QGraphicsItem::mouseMoveEvent(event);
}

void IndicatorItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event){
	update();
	QGraphicsItem::hoverEnterEvent(event);
}

void IndicatorItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event){
	update();
	QGraphicsItem::hoverLeaveEvent(event);

}

void IndicatorItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event){
	QStringList unit_list = g_units.getUnitList(unit);
	QMenu menu;
	QAction* actTrend = new QAction("Trend", this);
	connect(actTrend, SIGNAL(triggered()), this, SLOT(trend()));
	menu.addAction(actTrend);
	QMenu* actUnits = new QMenu("Unit...");
	foreach(QString unit_name, unit_list){
		QAction* actUnit = new QAction(unit_name, this);
		actUnit->setCheckable(true);
		actUnit->setChecked(unit_name == unit);
		actUnits->addAction(actUnit);
	}
	menu.addMenu(actUnits);
	menu.addSeparator();
	bool isTrendInTrendList = scene->common->isTrendInTrendList(indicator->getParent(), full_name);
	QAction* actAddToTrendList = new QAction(isTrendInTrendList ? "Already in trend list" : "Add to trend list", this);
	connect(actAddToTrendList, SIGNAL(triggered()), this, SLOT(add_to_trend_list()));
	actAddToTrendList->setEnabled(!isTrendInTrendList);
	menu.addAction(actAddToTrendList);
	QAction *aResult =  menu.exec(event->screenPos());
	if (aResult == NULL) return;
	QString result = aResult->text();
	if	(find(unit_list.begin(), unit_list.end(), result) != unit_list.end()){
		unit = result;
		update();
	}
}

void IndicatorItem::adjust(const QPointF &_pos){
	setX(_pos.x());
	setY(_pos.y());
}

void IndicatorItem::trend(){
	emit createTrend(full_name);
}

void IndicatorItem::add_to_trend_list(){
	automaticItem *item = indicator->getParent();
	scene->common->addTrendToTrendList(item, full_name, unit);
}
void IndicatorItem::setValue(const double &_value, const int &_digits){
	value = _value; 
	digits = _digits;
	update();
}

void IndicatorItem::setUnit(const QString &_unit){ 
	value = value * g_units.getCoeff(unit) / g_units.getCoeff(_unit);
	unit = _unit;	
	update();
};

void IndicatorItem::setIndicatorItemVisible(const bool &_visible){

}